home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Aminet 1 (Walnut Creek)
/
Aminet - June 1993 [Walnut Creek].iso
/
usenet
/
sources
/
volume91
/
utilitys
/
ed3_1_1
/
part02
/
undo.c
< prev
Wrap
C/C++ Source or Header
|
1991-11-07
|
15KB
|
655 lines
/*
ed3 functions to maintain the undo/redo buffers
10-17-91 fixed two pernicious bugs (what does that mean?)
10-16-91 undo and redo will do a full refresh is it involves moving points
and there aren't too many faces on the screen
10-03-91 add support for the join points action
10-02-91 main implementation
09-25-91 created (10 functions)
*/
#include "sysnogr.h"
int head, tail; /* action list offsets */
int del_here, add_here, final; /* operation list offsets */
/*
enough_room returns whether an operation requiring the creation of many
objects will overflow the action buffer or not. Should be called by
potentially large operations as a safety measure.
max_verts is the maximum number of vertices for the polys which
will be added OR deleted. A value of 0 is passed if thisis unknown.
Worst case will be assumed (MAX_EDGES vertices).
*/
int enough_room(p, f, q, dp, df, dq, mp, max_verts)
index p, f, q;
index dp, df, dq;
index mp;
int max_verts; /* the maximum vertices that any created/delete poly will have */
{
long bytes = 0;
/* there's always enough room if undo is turned off */
if (!flags.undo_active) return 1;
bytes += (p * sizeof(a_add_p));
bytes += (f * sizeof(a_add_f));
if (max_verts)
bytes += (q * (sizeof(a_add_q) - ((MAX_EDGES - max_verts) * sizeof(index))));
else
bytes += (q * sizeof(a_add_q)); /* we'll consider the worst case */
bytes += (dp * sizeof(a_del_p));
bytes += (df * sizeof(a_del_f));
if (max_verts)
bytes += (q * (sizeof(a_del_q) - ((MAX_EDGES - max_verts) * sizeof(index))));
else
bytes += (q * sizeof(a_del_q)); /* we'll consider the worst case */
bytes += (mp * sizeof(a_move_p));
return (bytes < (action_bufsize - (MAX_ACTION_SIZE * 2)));
}
void undo()
{
unsigned short actions;
unsigned short starting_offset;
unsigned short offset;
short act, act2;
int moved_flag = 0; /* becomes set if the undo involves and move_point actions */
if (!flags.undo_active) return;
if (add_here == del_here) return; /* nothing to undo */
add_here --;
changes--;
if (add_here < 0) add_here = operation_bufsize-1; /* wrap around */
actions = operation[add_here].actions;
starting_offset = operation[add_here].offset;
/* undo each of the actions in reverse order */
for (act = actions-1; act >= 0; act--)
{
/* scan forward to find the offset of this (act) action */
offset = starting_offset;
for (act2 = 0; act2 < act; act2++)
{
offset += size_of_action(offset);
if ((action_bufsize - offset) < MAX_ACTION_SIZE) /* wrap around */
offset = 0;
}
if (*((char *)(action+offset)) == AT_MOVE_P) moved_flag = 1;
/* now actually undo the action */
undo_action(offset);
}
show_counts(); /* we've probably changed the number of faces etc. */
if (moved_flag && (faces+polys < REFRESH_LEVEL)) refresh_all();
}
void undo_action(offset)
unsigned short offset;
{
a_add_p *aap;
a_del_p *adp;
a_add_f *aaf;
a_del_f *adf;
a_add_q *aaq;
a_del_q *adq;
a_move_p *amp;
a_join_p *ajp;
a_del_v *adv;
a_morph_q *amq;
index i1;
switch (*((char *)(action+offset))) /* switch on type */
{
case AT_ADD_P:
aap = (a_add_p *)(action+offset);
draw_point(points-1, colors.erase);
points--; /* it's this simple */
break;
case AT_DEL_P:
adp = (a_del_p *)(action+offset);
insert_point(adp->number, adp->x, adp->y, adp->z);
draw_point(adp->number, colors.point);
break;
case AT_ADD_F:
aaf = (a_add_f *)(action+offset);
del_face(faces-1, 1); /* silently delete */
break;
case AT_DEL_F:
adf = (a_del_f *)(action+offset);
insert_face(adf->number, adf->p0, adf->p1, adf->p2, adf->color);
draw_face_color(adf->number, colors.face);
break;
case AT_ADD_Q:
aaq = (a_add_q *)(action+offset);
del_poly(polys-1, 1); /* silently delete */
break;
case AT_DEL_Q:
adq = (a_del_q *)(action+offset);
insert_poly(adq->number, adq->points, adq->p, adq->color);
draw_poly_color(adq->number, colors.face);
break;
case AT_MOVE_P:
amp = (a_move_p *)(action+offset);
i1 = amp->p0;
draw_point(i1, colors.erase);
point[i1].x -= amp->dx;
point[i1].y -= amp->dy;
point[i1].z -= amp->dz;
convert_point(i1);
draw_point(i1, colors.point);
break;
case AT_JOIN_P:
ajp = (a_join_p *)(action+offset);
for (i1 = 0; i1 < ajp->changes; i1++)
{
change_facet(ajp->p0, ajp->p1, ajp->changed[i1]);
}
break;
case AT_DEL_V:
adv = (a_del_v *)(action+offset);
insert_vertex(adv->q, adv->vert, adv->p0, 1);
break;
case AT_MORPH_Q:
amq = (a_morph_q *)(action+offset);
del_face(faces-1, 1);
break;
}
}
/* change the reference to point p0 in facet i1 to point p1 */
void change_facet(p0, p1, i1)
index p0, p1, i1;
{
short vert;
if (i1 & POLYFLAG)
{
i1 &= ~POLYFLAG; /* turn flag off */
for (vert = 0; vert < poly[i1].verts; vert++)
if (poly[i1].p[vert] == p0)
poly[i1].p[vert] = p1;
}
else
{
for (vert = 0; vert < 3; vert++)
if (face[i1].p[vert] == p0)
face[i1].p[vert] = p1;
}
}
void redo()
{
unsigned short actions;
unsigned short starting_offset;
unsigned short offset;
short act;
int moved_flag = 0; /* becomes set if the undo involves and move_point actions */
if (!flags.undo_active) return;
if (add_here == final) return; /* nothing to redo */
actions = operation[add_here].actions;
starting_offset = operation[add_here].offset;
/* redo each of the actions in forward order */
offset = starting_offset;
for (act = 0; act < actions; act++)
{
if (*((char *)(action+offset)) == AT_MOVE_P) moved_flag = 1;
redo_action(offset); /* now actually redo the action */
offset += size_of_action(offset);
}
add_here ++;
changes ++;
show_counts(); /* we've probably changed the number of faces etc. */
if (moved_flag && (faces+polys < REFRESH_LEVEL)) refresh_all();
}
void redo_action(offset)
unsigned short offset;
{
a_add_p *aap;
a_del_p *adp;
a_add_f *aaf;
a_del_f *adf;
a_add_q *aaq;
a_del_q *adq;
a_move_p *amp;
a_join_p *ajp;
a_del_v *adv;
a_morph_q *amq;
index i1;
switch (*((char *)(action+offset))) /* switch on type */
{
case AT_ADD_P:
aap = (a_add_p *)(action+offset);
add_point0(aap->x, aap->y, aap->z);
draw_point(points-1, colors.point);
break;
case AT_DEL_P:
adp = (a_del_p *)(action+offset);
draw_point(adp->number, colors.erase);
del_point(adp->number, 1, 1);
break;
case AT_ADD_F:
aaf = (a_add_f *)(action+offset);
add_face0(aaf->p0, aaf->p1, aaf->p2, aaf->color);
draw_face_color(faces-1, colors.face);
break;
case AT_DEL_F:
adf = (a_del_f *)(action+offset);
del_face(adf->number, 1); /* silently delete */
break;
case AT_ADD_Q:
aaq = (a_add_q *)(action+offset);
add_poly(aaq->points, aaq->color, aaq->p, 1);
draw_poly_color(polys-1, colors.face);
break;
case AT_DEL_Q:
adq = (a_del_q *)(action+offset);
del_poly(adq->number, 1); /* silently delete */
break;
case AT_MOVE_P:
amp = (a_move_p *)(action+offset);
i1 = amp->p0;
draw_point(i1, colors.erase);
point[i1].x += amp->dx;
point[i1].y += amp->dy;
point[i1].z += amp->dz;
convert_point(i1);
draw_point(i1, colors.point);
break;
case AT_JOIN_P:
ajp = (a_join_p *)(action+offset);
for (i1 = 0; i1 < ajp->changes; i1++)
{
change_facet(ajp->p1, ajp->p0, ajp->changed[i1]);
}
break;
case AT_DEL_V:
adv = (a_del_v *)(action+offset);
delete_vertex(adv->q, adv->vert, 1);
break;
case AT_MORPH_Q:
amq = (a_morph_q *)(action+offset);
poly_to_face(amq->q, 1);
break;
}
}
unsigned short size_of_action(offset)
unsigned short offset;
{
a_add_q *aaq = (a_add_q *)(action + offset);
a_del_q *adq = (a_del_q *)(action + offset);
a_join_p *ajp = (a_join_p *)(action + offset);
switch (*((char *)(action+offset))) /* switch on type */
{
case AT_ADD_P: return sizeof(a_add_p);
case AT_DEL_P: return sizeof(a_del_p);
case AT_ADD_F: return sizeof(a_add_f);
case AT_DEL_F: return sizeof(a_del_f);
case AT_ADD_Q:
return (sizeof(a_add_q) -
((MAX_EDGES - aaq->points) * sizeof(index)));
case AT_DEL_Q:
return (sizeof(a_del_q) -
((MAX_EDGES - adq->points) * sizeof(index)));
case AT_MOVE_P: return sizeof(a_move_p);
case AT_JOIN_P:
return (sizeof(a_join_p) -
((MAX_EDGES - ajp->changes) * sizeof(index)));
case AT_DEL_V: return sizeof(a_del_v);
case AT_MORPH_Q: return sizeof(a_morph_q);
}
}
void action_add_p(x, y, z)
coord x, y, z;
{
a_add_p *new;
if (!flags.undo_active) return;
make_room();
new = (a_add_p *)(action + head);
new->type = AT_ADD_P;
new->x = x;
new->y = y;
new->z = z;
head += sizeof(a_add_p);
operation[add_here].actions++;
}
void action_del_p(p, x, y, z)
index p;
coord x, y, z;
{
a_del_p *new;
if (!flags.undo_active) return;
make_room();
new = (a_del_p *)(action + head);
new->type = AT_DEL_P;
new->x = x;
new->y = y;
new->z = z;
new->number = p;
head += sizeof(a_del_p);
operation[add_here].actions++;
}
void action_add_f(p0, p1, p2, col)
index p0, p1, p2;
int col;
{
a_add_f *new;
if (!flags.undo_active) return;
make_room();
new = (a_add_f *)(action + head);
new->type = AT_ADD_F;
new->p0 = p0;
new->p1 = p1;
new->p2 = p2;
new->color = col;
head += sizeof(a_add_f);
operation[add_here].actions++;
}
void action_del_f(f)
index f;
{
a_del_f *new;
if (!flags.undo_active) return;
make_room();
new = (a_del_f *)(action + head);
new->type = AT_DEL_F;
new->color = face[f].color;
new->p0 = face[f].p[0];
new->p1 = face[f].p[1];
new->p2 = face[f].p[2];
new->number = f;
head += sizeof(a_del_f);
operation[add_here].actions++;
}
void action_add_q(points, p, col)
int points;
index *p;
int col;
{
a_add_q *new;
int pc; /* point counter */
int action_size;
if (!flags.undo_active) return;
make_room();
new = (a_add_q *)(action + head);
new->type = AT_ADD_Q;
new->points = points;
new->color = col;
action_size = sizeof(a_add_q) - ((MAX_EDGES-points) * sizeof(index));
for (pc = 0; pc < points; pc++)
new->p[pc] = p[pc];
head += action_size;
operation[add_here].actions++;
}
void action_del_q(q)
index q;
{
a_del_q *new;
int pc; /* politically correct personal computer point counter */
int action_size;
int verts;
if (!flags.undo_active) return;
make_room();
verts = poly[q].verts;
new = (a_del_q *)(action + head);
new->type = AT_DEL_Q;
new->points = verts;
new->color = poly[q].color;
new->number = q;
action_size = sizeof(a_del_q) - ((MAX_EDGES-verts) * sizeof(index));
for (pc = 0; pc < verts; pc++)
new->p[pc] = poly[q].p[pc];
head += action_size;
operation[add_here].actions++;
}
void action_move_p(p, dx, dy, dz)
index p;
coord dx, dy, dz;
{
a_move_p *new;
if (!flags.undo_active) return;
make_room();
new = (a_move_p *)(action + head);
new->type = AT_MOVE_P;
new->p0 = p;
new->dx = dx;
new->dy = dy;
new->dz = dz;
head += sizeof(a_move_p);
operation[add_here].actions++;
}
void action_join_p(p0, p1, changed, changes)
index p0, p1;
index *changed;
index changes;
{
a_join_p *new;
int action_size;
short i1;
if (!flags.undo_active) return;
make_room();
new = (a_join_p *)(action + head);
new->type = AT_JOIN_P;
new->p0 = p0;
new->p1 = p1;
new->changes = changes;
for (i1 = 0; i1 < changes; i1++)
new->changed[i1] = changed[i1];
action_size = sizeof(a_join_p) - ((MAX_EDGES-changes) * sizeof(index));
head += action_size;
operation[add_here].actions++;
}
void action_del_v(q, vert, p0)
index q, p0;
char vert;
{
a_del_v *new;
if (!flags.undo_active) return;
make_room();
new = (a_del_v *)(action + head);
new->type = AT_DEL_V;
new->q = q;
new->vert = vert;
new->p0 = p0;
head += sizeof(a_del_v);
operation[add_here].actions++;
}
void action_morph_q(q)
index q;
{
a_morph_q *new;
if (!flags.undo_active) return;
make_room();
new = (a_morph_q *)(action + head);
new->type = AT_MORPH_Q;
new->q = q;
head += sizeof(a_morph_q);
operation[add_here].actions++;
}
void make_room()
{
int usable; /* usable room ahead of the insertion (head) point */
int ok = 0;
if (flags.debug) printf("\tmake_room(%d, %d) ->", tail, head);
while (!ok)
{
if (head >= tail)
{
usable = action_bufsize - head;
if (usable <= MAX_ACTION_SIZE) /* end of buffer */
{
head = 0; /* wrap around */
/* wrapped around onto a full buffer? */
if (tail == 0)
/* put some space between the offsets */
delete_oldest_operation();
}
else
ok = 1;
}
if (head < tail) /* this is not an else because head can change above */
{
usable = tail - head;
if (usable <= MAX_ACTION_SIZE) /* not enough room */
delete_oldest_operation(); /* make some more room */
else
ok = 1;
}
}
if (flags.debug) printf("(%d, %d)\n", tail, head);
}
void delete_oldest_operation()
{
int act = operation[del_here].actions;
int offset = operation[del_here].offset;
int ac;
int size;
if (flags.debug) printf("delete_oldest (%d %d) (add %d del %d) ", tail, head, add_here, del_here);
if (offset != tail)
{
if (flags.debug) printf("tail %d, offset of oldest action %d\n", tail, offset);
sys_exit("bad circular unfo buffer logic");
}
for (ac = 0; ac < act; ac++)
{
size = size_of_action(offset);
if (flags.debug) printf(" (deleting %d) ", size);
tail += size;
offset += size;
if ((action_bufsize - tail) < MAX_ACTION_SIZE)
tail = offset = 0;
}
del_here++;
if (del_here >= operation_bufsize) del_here = 0; /* wrap around */
}
/* call at startup or whenever you need to destroy all history
(like when loading or clearing the entire project) */
void init_undo()
{
head = tail = 0;
del_here = add_here = 0;
memset(action, 0, action_bufsize); /* clear buffer */
changes = 0;
}
void begin_operation()
{
if (!flags.undo_active) return;
if (flags.debug) printf("begin:(%d, %d)\n", tail, head);
/* maybe the operation buffer is almost full */
if ((add_here+1)%operation_bufsize == del_here)
delete_oldest_operation();
if (add_here != final) /* if we have redone some operations */
{
/* they are lost when we begin a new operation */
final = add_here;
/* truncate the action list accordingly */
head = operation[add_here].offset;
}
/* make sure that the head offset we have at this point
is OK before storing it in the operation structure */
make_room();
if (flags.debug) printf("offset of action %d is %d\n", add_here, head);
operation[add_here].offset = head;
operation[add_here].actions = 0;
}
void end_operation()
{
if (!flags.undo_active) return;
if (flags.debug) printf("end:(%d, %d)\n\n", tail, head);
add_here++;
changes++;
if (add_here >= operation_bufsize) add_here = 0; /* wrap around */
final = add_here;
}